#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <asm/io.h>

#define	   GPX2CONF		(0x11000c40)
#define	   GPX2DATA		(0x11000c44)


struct dev_pri {
	int irqno;
	int led_stat;		/*0-off 1-on*/

	void *gpx2conf;
	void *gpx2data;

	char buf[32];
};

struct dev_pri devpri;



void led_init(void)
{

	int val;
	
	devpri.gpx2conf = ioremap(GPX2CONF,4);
	devpri.gpx2data = ioremap(GPX2DATA,4);

	/*配置 gpx2 7 管脚为输出模式*/
	val = readl(devpri.gpx2conf);
	val = val & ( ~( 0xf<<28 ) );
	val = val |  (1<<28);
	writel(val,devpri.gpx2conf);

	/*关灯*/
	val = readl(devpri.gpx2data);
	val = val &  ( ~(1<<7) );
	writel(val,devpri.gpx2data);

	return  ;
}

void led_ctrl(int on)
{
	int val;

	if(on == 0){
		/*关灯*/
		val = readl(devpri.gpx2data);
		val = val &  ( ~(1<<7) );
		writel(val,devpri.gpx2data);
	}else {
		/*关灯*/
		val = readl(devpri.gpx2data);
		val = val | (1<<7);
		writel(val,devpri.gpx2data);
	}
}



irqreturn_t  irq_handler_fun (int irqno, void *args)
{
	struct dev_pri *pri = args;

	pri->led_stat = 1 - pri->led_stat;


	led_ctrl(pri->led_stat);

	printk("%s->%d  irqno%d %d\n",__func__,__LINE__,irqno,pri->irqno);


	return IRQ_HANDLED; 		/*
		如果你的中断执行ok,那么 返回 IRQ_HANDLED
		如果你的中断 执行不ok,  返回  IRQ_NONE
	*/
}



int  md_init (void )
{
	struct device_node * of_node;
	int irqno;
	int ret;

	/*
	如何实现 中断驱动
	1.定义并初始化 中断对象
		1)中断号	从设备树的 节点(struct device_node)中提出
		     -1- 找到 设备树节点 
		     	struct device_node *of_find_node_by_path(const char * path)
		        path,你要寻找的节点的路径  "/srom-sc1@xxx/ethernet@"
			 -2- 从节点中 提取出来 中断号
				int irq_of_parse_and_map(struct device_node * dev,int index)
			 	dev, 设备树节点指针
			 	index,  一个设备可以有很多个中断, 你要指定 你这一次需要哪一个,从0开始
			 	返回
			 		正数   中断号
			 		负数   错误
		2)实现中断方法
		
	2.将对象加入到 内核中去
		
		int request_irq(unsigned int irqno,irq_handler_t handler,
				unsigned long flags,const char * name,void * dev)

		irqno,中断号
		handler,中断函数
		flags,  有很多bit,每一个bit表示一种功能,  可以用于指定中断触发方式
		name, 给中断 骑个名字, 自己起
		dev, 你给该中断 指定 私有数据

		返回值 
			负值,失败
			0 成功

	3.将中断删除
	
		void free_irq(unsigned int irqno,void * dev_id);
		irqno,你要释放的中断号
		dev_id, 和你request_irq的 dev保持一致
		
	*/

	

	/*找到 按键的节点,返回 该节点指针*/
	of_node = of_find_node_by_path("/key2node");
	if(!of_node ){
		printk("%s->%d of_find_node_by_path err\n",__func__,__LINE__);
		return -13;
	}

	irqno = irq_of_parse_and_map(of_node,0);
	if(irqno <0){
		printk("%s->%d irq_of_parse_and_map err\n",__func__,__LINE__);
		return -14;
	}

	printk("%s->%d get key2node irqno %d\n",__func__,__LINE__,irqno);


	devpri.irqno = irqno;

	ret = request_irq(irqno,irq_handler_fun,
				IRQF_TRIGGER_FALLING,"key2nodeintr",&devpri);
	if(ret <0){
		printk("%s->%d request_irq err\n",__func__,__LINE__);
		return -15;
	}

	devpri.led_stat = 0;
	
	led_init();		

	printk("%s->%d\n",__func__,__LINE__);
	return 0;	
}


void  md_exit (void )
{

	/*释放中断*/
	free_irq(devpri.irqno,&devpri);

	printk("%s->%d irqno%d\n",__func__,__LINE__,devpri.irqno);
}



module_init(md_init);
module_exit(md_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("WQF   wanglaoshi@126.com");
MODULE_DESCRIPTION(" this module description : a testcode for modules");
